/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 1.1 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
*/

/*
* subdivider.cxx
*
*/

#include "glimports.h"
#include "myassert.h"
#include "mystdio.h"
#include "subdivider.h"
#include "arc.h"
#include "bezierarc.h"
#include "bin.h"
#include "renderhints.h"
#include "backend.h"
#include "mapdesc.h"
#include "quilt.h"
#include "patchlist.h"
#include "patch.h"
#include "nurbsconsts.h"
#include "trimvertpool.h"
#include "simplemath.h"

#include "polyUtil.h" //for function area()

Bin* Subdivider::makePatchBoundary(const REAL* from, const REAL* to)
{
   Bin* ret=new Bin();
   REAL smin=from[0];
   REAL smax=to[0];
   REAL tmin=from[1];
   REAL tmax=to[1];

   pjarc=0;

   Arc_ptr jarc=new(arcpool) Arc(arc_bottom, 0);
   arctessellator.bezier(jarc, smin, smax, tmin, tmin);
   ret->addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_right, 0);
   arctessellator.bezier(jarc, smax, smax, tmin, tmax);
   ret->addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_top, 0);
   arctessellator.bezier(jarc, smax, smin, tmax, tmax);
   ret->addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_left, 0);
   arctessellator.bezier(jarc, smin, smin, tmax, tmin);
   ret->addarc(jarc);
   jarc->append(pjarc);

   assert(jarc->check()!=0);

   return ret;
}

/*---------------------------------------------------------------------------
* Subdivider - construct a subdivider
*---------------------------------------------------------------------------
*/

Subdivider::Subdivider(Renderhints& r, Backend& b): slicer(b),
                       arctessellator(trimvertexpool, pwlarcpool),
                       arcpool(sizeof(Arc), 1, "arcpool"),
                       bezierarcpool(sizeof(BezierArc), 1, "Bezarcpool"),
                       pwlarcpool(sizeof(PwlArc), 1, "Pwlarcpool"),
                       renderhints(r),
                       backend(b)
{
}

void Subdivider::setJumpbuffer(JumpBuffer* j)
{
   jumpbuffer=j;
}

/*---------------------------------------------------------------------------
* clear - reset all state after possible error condition
*---------------------------------------------------------------------------
*/

void Subdivider::clear(void)
{
    trimvertexpool.clear();
    arcpool.clear();
    pwlarcpool.clear();
    bezierarcpool.clear();
}

/*---------------------------------------------------------------------------
* ~Subdivider - destroy a subdivider
*---------------------------------------------------------------------------
*/

Subdivider::~Subdivider(void)
{
}

/*---------------------------------------------------------------------------
* addArc - add a bezier arc to a trim loop and to a bin
*---------------------------------------------------------------------------
*/
void Subdivider::addArc(REAL* cpts, Quilt* quilt, long _nuid)
{
   BezierArc* bezierArc=new(bezierarcpool) BezierArc;
   Arc* jarc=new(arcpool) Arc(arc_none, _nuid);
   jarc->pwlArc=0;
   jarc->bezierArc=bezierArc;
   bezierArc->order=quilt->qspec->order;
   bezierArc->stride=quilt->qspec->stride;
   bezierArc->mapdesc=quilt->mapdesc;
   bezierArc->cpts=cpts;
   initialbin.addarc(jarc);
   pjarc=jarc->append(pjarc);
}

/*---------------------------------------------------------------------------
* addArc - add a pwl arc to a trim loop and to a bin
*---------------------------------------------------------------------------
*/

void Subdivider::addArc(int npts, TrimVertex* pts, long _nuid)
{
   Arc* jarc=new(arcpool) Arc(arc_none, _nuid);
   jarc->pwlArc=new(pwlarcpool) PwlArc(npts, pts);
   initialbin.addarc(jarc);
   pjarc=jarc->append(pjarc);
}

void Subdivider::beginQuilts(void)
{
   qlist=0;
}

void Subdivider::addQuilt(Quilt* quilt)
{
   quilt->next=qlist;
   qlist=quilt;
}

/*---------------------------------------------------------------------------
* drawSurfaces - main entry point for surface tessellation
*---------------------------------------------------------------------------
*/

void Subdivider::drawSurfaces(long nuid)
{
   renderhints.init();

   if (qlist==NULL)
   {
      //initialbin could be nonempty due to some errors
      freejarcs(initialbin);
      return;
   }

   for (Quilt* q=qlist; q; q=q->next)
   {
      if (q->isCulled()==CULL_TRIVIAL_REJECT)
      {
         freejarcs(initialbin);
         return;
      }
   }

   REAL from[2], to[2];
   qlist->getRange(from, to, spbrkpts, tpbrkpts);

   // perform optimization only when the samplng method is
   // DOMAIN_DISTANCE and the display methdo is either
   // fill or outline_polygon.
   int optimize=(is_domain_distance_sampling && (renderhints.display_method!=N_OUTLINE_PATCH));

   if (!initialbin.isnonempty())
   {
      if (!optimize)
      {
         makeBorderTrim(from, to);
      }
   }
   else
   {
      REAL rate[2];
      qlist->findRates(spbrkpts, tpbrkpts, rate);

      if (decompose(initialbin, min(rate[0], rate[1])))
      {
         mylongjmp(jumpbuffer, 31);
      }
   }

   backend.bgnsurf(renderhints.wiretris, renderhints.wirequads, nuid);

   if ((!initialbin.isnonempty()) && optimize)
   {
      int i, j;
      int num_u_steps;
      int num_v_steps;
      for (i=spbrkpts.start; i<spbrkpts.end-1; i++)
      {
         for (j=tpbrkpts.start; j<tpbrkpts.end-1; j++)
         {
            Real pta[2], ptb[2];
            pta[0]=spbrkpts.pts[i];
            ptb[0]=spbrkpts.pts[i+1];
            pta[1]=tpbrkpts.pts[j];
            ptb[1]=tpbrkpts.pts[j+1];
            qlist->downloadAll(pta, ptb, backend);

            num_u_steps=(int)(domain_distance_u_rate*(ptb[0]-pta[0]));
            num_v_steps=(int)(domain_distance_v_rate*(ptb[1]-pta[1]));

            if (num_u_steps<=0)
            {
               num_u_steps=1;
            }
            if (num_v_steps<=0)
            {
               num_v_steps=1;
            }

            backend.surfgrid(pta[0], ptb[0], num_u_steps,
                             ptb[1], pta[1], num_v_steps);
            backend.surfmesh(0, 0, num_u_steps,num_v_steps);

            continue;
         }
      }
   }
   else
   {
      subdivideInS(initialbin);
   }

   backend.endsurf();
}

void Subdivider::subdivideInS(Bin& source)
{
   if (renderhints.display_method==N_OUTLINE_PARAM)
   {
      outline(source);
      freejarcs(source);
   }
   else
   {
      setArcTypeBezier();
      setNonDegenerate();
      splitInS(source, spbrkpts.start, spbrkpts.end);
   }
}

/*---------------------------------------------------------------------------
* splitInS - split a patch and a bin by an isoparametric line
*---------------------------------------------------------------------------
*/

void Subdivider::splitInS(Bin& source, int start, int end)
{
   if (source.isnonempty())
   {
      if (start!=end)
      {
         int i=start+(end-start)/2;
         Bin left, right;
         split(source, left, right, 0, spbrkpts.pts[i]);
         splitInS(left, start, i);
         splitInS(right, i+1, end);
      }
      else
      {
         if (start==spbrkpts.start || start==spbrkpts.end)
         {
            freejarcs(source);
         }
         else
         {
            if (renderhints.display_method==N_OUTLINE_PARAM_S)
            {
               outline(source);
               freejarcs(source);
            }
            else
            {
               setArcTypeBezier();
               setNonDegenerate();
               s_index=start;
               splitInT(source, tpbrkpts.start, tpbrkpts.end);
            }
         }
      }
   }
}

/*---------------------------------------------------------------------------
* splitInT - split a patch and a bin by an isoparametric line
*---------------------------------------------------------------------------
*/

void Subdivider::splitInT(Bin& source, int start, int end)
{
   if (source.isnonempty())
   {
      if (start!=end)
      {
         int i=start+(end-start)/2;
         Bin left, right;
         split(source, left, right, 1, tpbrkpts.pts[i]);
         splitInT(left, start, i);
         splitInT(right, i+1, end);
      }
      else
      {
         if (start==tpbrkpts.start || start==tpbrkpts.end)
         {
            freejarcs(source);
         }
         else
         {
            if (renderhints.display_method==N_OUTLINE_PARAM_ST)
            {
               outline(source);
               freejarcs(source);
            }
            else
            {
               t_index=start;
               setArcTypeBezier();
               setDegenerate();

               REAL pta[2], ptb[2];
               pta[0]=spbrkpts.pts[s_index-1];
               pta[1]=tpbrkpts.pts[t_index-1];

               ptb[0]=spbrkpts.pts[s_index];
               ptb[1]=tpbrkpts.pts[t_index];
               qlist->downloadAll(pta, ptb, backend);

               Patchlist patchlist(qlist, pta, ptb);
               samplingSplit(source, patchlist, renderhints.maxsubdivisions, 0);
               setNonDegenerate();
               setArcTypeBezier();
            }
         }
      }
   }
}

/*--------------------------------------------------------------------------
* samplingSplit - recursively subdivide patch, cull check each subpatch
*--------------------------------------------------------------------------
*/

void Subdivider::samplingSplit(Bin& source, Patchlist& patchlist, int subdivisions, int param)
{
   if (!source.isnonempty())
   {
      return;
   }

   if (patchlist.cullCheck()==CULL_TRIVIAL_REJECT)
   {
      freejarcs(source);
      return;
   }

   patchlist.getstepsize();

   if (renderhints.display_method==N_OUTLINE_PATCH)
   {
      tessellation(source, patchlist);
      outline(source);
      freejarcs(source);
      return;
   }

   tessellation(source, patchlist);

   if (patchlist.needsSamplingSubdivision() && (subdivisions>0))
   {
      if (!patchlist.needsSubdivision(0))
      {
         param=1;
      }
      else
      {
         if (! patchlist.needsSubdivision(1))
         {
            param=0;
         }
         else
         {
            param=1-param;
         }
      }

      Bin left, right;
      REAL mid=(patchlist.pspec[param].range[0]+patchlist.pspec[param].range[1])*0.5f;
      split(source, left, right, param, mid);
      Patchlist subpatchlist(patchlist, param, mid);
      samplingSplit(left, subpatchlist, subdivisions-1, param);
      samplingSplit(right, patchlist, subdivisions-1, param);
   }
   else
   {
      setArcTypePwl();
      setDegenerate();
      nonSamplingSplit(source, patchlist, subdivisions, param);
      setDegenerate();
      setArcTypeBezier();
   }
}

void Subdivider::nonSamplingSplit(Bin& source, Patchlist& patchlist,
                                  int subdivisions, int param)
{
   if (patchlist.needsNonSamplingSubdivision() && (subdivisions>0))
   {
      param=1-param;

      Bin left, right;
      REAL mid=(patchlist.pspec[param].range[0]+patchlist.pspec[param].range[1])*0.5f;
      split(source, left, right, param, mid);
      Patchlist subpatchlist(patchlist, param, mid);
      if (left.isnonempty())
      {
         if (subpatchlist.cullCheck()==CULL_TRIVIAL_REJECT)
         {
            freejarcs(left);
         }
         else
         {
            nonSamplingSplit(left, subpatchlist, subdivisions-1, param);
         }
      }
      if (right.isnonempty())
      {
         if (patchlist.cullCheck()==CULL_TRIVIAL_REJECT)
         {
            freejarcs(right);
         }
         else
         {
            nonSamplingSplit(right, patchlist, subdivisions-1, param);
         }
      }
   }
   else
   {
      // make bbox calls
      patchlist.bbox();
      backend.patch(patchlist.pspec[0].range[0], patchlist.pspec[0].range[1],
                    patchlist.pspec[1].range[0], patchlist.pspec[1].range[1]);

      if (renderhints.display_method==N_OUTLINE_SUBDIV)
      {
         outline(source);
         freejarcs(source);
      }
      else
      {
         setArcTypePwl();
         setDegenerate();
         findIrregularS(source);
         monosplitInS(source, smbrkpts.start, smbrkpts.end);
      }
   }
}

/*--------------------------------------------------------------------------
* tessellation - set tessellation of interior and boundary of patch
*--------------------------------------------------------------------------
*/

void Subdivider::tessellation(Bin& bin, Patchlist &patchlist)
{
   // tessellate unsampled trim curves
   tessellate(bin, patchlist.pspec[1].sidestep[1], patchlist.pspec[0].sidestep[1],
                   patchlist.pspec[1].sidestep[0], patchlist.pspec[0].sidestep[0]);

   // set interior sampling rates
   slicer.setstriptessellation(patchlist.pspec[0].stepsize, patchlist.pspec[1].stepsize);

   //added by zl: set the order which will be used in slicer.c++
   slicer.set_ulinear((patchlist.get_uorder()==2));
   slicer.set_vlinear((patchlist.get_vorder()==2));

   // set boundary sampling rates
   stepsizes[0]=patchlist.pspec[1].stepsize;
   stepsizes[1]=patchlist.pspec[0].stepsize;
   stepsizes[2]=patchlist.pspec[1].stepsize;
   stepsizes[3]=patchlist.pspec[0].stepsize;
}

/*---------------------------------------------------------------------------
* monosplitInS - split a patch and a bin by an isoparametric line
*---------------------------------------------------------------------------
*/

void Subdivider::monosplitInS(Bin& source, int start, int end)
{
   if (source.isnonempty())
   {
      if (start!=end)
      {
         int i=start+(end-start)/2;
         Bin left, right;
         split(source, left, right, 0, smbrkpts.pts[i]);
         monosplitInS(left, start, i);
         monosplitInS(right, i+1, end);
      }
      else
      {
         if (renderhints.display_method==N_OUTLINE_SUBDIV_S)
         {
            outline(source);
            freejarcs(source);
         }
         else
         {
            setArcTypePwl();
            setDegenerate();
            findIrregularT(source);
            monosplitInT(source, tmbrkpts.start, tmbrkpts.end);
         }
      }
   }
}

/*---------------------------------------------------------------------------
* monosplitInT - split a patch and a bin by an isoparametric line
*---------------------------------------------------------------------------
*/

void Subdivider::monosplitInT(Bin& source, int start, int end)
{
   if (source.isnonempty())
   {
      if (start!=end)
      {
         int i=start+(end-start)/2;
         Bin left, right;
         split(source, left, right, 1, tmbrkpts.pts[i]);
         monosplitInT(left, start, i);
         monosplitInT(right, i+1, end);
      }
      else
      {
         if (renderhints.display_method==N_OUTLINE_SUBDIV_ST)
         {
            outline(source);
            freejarcs(source);
         }
         else
         {
            render(source);
            freejarcs(source);
         }
      }
   }
}

/*----------------------------------------------------------------------------
* findIrregularS - determine points of non-monotonicity is s direction
*----------------------------------------------------------------------------
*/

void Subdivider::findIrregularS(Bin& bin)
{
   assert(bin.firstarc()->check()!=0);

   smbrkpts.grow(bin.numarcs());

   for (Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc())
   {
      REAL* a=jarc->prev->tail();
      REAL* b=jarc->tail();
      REAL* c=jarc->head();

      if (b[1]==a[1] && b[1]==c[1])
      {
         continue;
      }

      // corrected code
      if ((b[1]<=a[1] && b[1]<=c[1]) || (b[1]>=a[1] && b[1]>=c[1]))
      {
         //each arc (jarc, jarc->prev, jarc->next) is a 
         //monotone arc consisting of multiple line segements.
         //it may happen that jarc->prev and jarc->next are the same,
         //that is, jarc->prev and jarc form a closed loop.
         //In such case, a and c will be the same.
         if (a[0]==c[0] && a[1]==c[1])
         {
            if (jarc->pwlArc->npts>2)
            {
               c=jarc->pwlArc->pts[jarc->pwlArc->npts-2].param;
            }
            else
            {
               assert(jarc->prev->pwlArc->npts>2);
               a=jarc->prev->pwlArc->pts[jarc->prev->pwlArc->npts-2].param;
            }
         }
         if (area(a, b, c)<0)
         {
            smbrkpts.add(b[0]);
         }
      }
   }

   smbrkpts.filter();
}

/*----------------------------------------------------------------------------
* findIrregularT - determine points of non-monotonicity in t direction
*                  where one arc is parallel to the s axis.
*----------------------------------------------------------------------------
*/

void Subdivider::findIrregularT(Bin& bin)
{
   assert(bin.firstarc()->check()!=0);

   tmbrkpts.grow(bin.numarcs());

   for (Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc())
   {
      REAL* a=jarc->prev->tail();
      REAL* b=jarc->tail();
      REAL* c=jarc->head();

      if (b[0]==a[0] && b[0]==c[0])
      {
         continue;
      }

      if (b[0]<=a[0] && b[0]<=c[0])
      {
         if (a[1]!=b[1] && b[1]!=c[1])
         {
            continue;
         }
         if (!ccwTurn_sr(jarc->prev, jarc))
         {
            tmbrkpts.add(b[1]);
         }
      }
      else
      {
         if (b[0]>=a[0] && b[0]>=c[0])
         {
            if (a[1]!=b[1] && b[1]!=c[1])
            {
               continue;
            }
            if (!ccwTurn_sl(jarc->prev, jarc))
            {
               tmbrkpts.add(b[1]);
            }
         }
      }
   }
   tmbrkpts.filter();
}

/*-----------------------------------------------------------------------------
* makeBorderTrim - if no user input trimming data then create
* a trimming curve around the boundaries of the Quilt.  The curve consists of
* four Jordan arcs, one for each side of the Quilt, connected, of course,
* head to tail.
*-----------------------------------------------------------------------------
*/

void Subdivider::makeBorderTrim(const REAL* from, const REAL* to)
{
   REAL smin=from[0];
   REAL smax=to[0];
   REAL tmin=from[1];
   REAL tmax=to[1];

   pjarc=0;

   Arc_ptr jarc=new(arcpool) Arc(arc_bottom, 0);
   arctessellator.bezier(jarc, smin, smax, tmin, tmin);
   initialbin.addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_right, 0);
   arctessellator.bezier(jarc, smax, smax, tmin, tmax);
   initialbin.addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_top, 0);
   arctessellator.bezier(jarc, smax, smin, tmax, tmax);
   initialbin.addarc(jarc);
   pjarc=jarc->append(pjarc);

   jarc=new(arcpool) Arc(arc_left, 0);
   arctessellator.bezier(jarc, smin, smin, tmax, tmin);
   initialbin.addarc(jarc);
   jarc->append(pjarc);

   assert(jarc->check()!=0);
}

/*----------------------------------------------------------------------------
* render - renders all monotone regions in a bin and frees the bin
*----------------------------------------------------------------------------
*/

void Subdivider::render(Bin& bin)
{
   bin.markall();

#ifdef N_ISOLINE_S
   slicer.setisolines((renderhints.display_method==N_ISOLINE_S) ? 1 : 0);
#else
   slicer.setisolines(0);
#endif

   for (Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc())
   {
      if (jarc->ismarked())
      {
         assert(jarc->check()!=0);
         Arc_ptr jarchead=jarc;
         do {
            jarc->clearmark();
            jarc=jarc->next;
         } while (jarc!=jarchead);
         slicer.slice(jarc);
      }
   }
}

/*---------------------------------------------------------------------------
* outline - render the trimmed patch by outlining the boundary 
*---------------------------------------------------------------------------
*/

void Subdivider::outline(Bin& bin)
{
   bin.markall();
   for (Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc())
   {
      if (jarc->ismarked())
      {
         assert(jarc->check()!=0);
         Arc_ptr jarchead=jarc;
         do {
            slicer.outline(jarc);
            jarc->clearmark();
            jarc=jarc->prev;
         } while (jarc!=jarchead);
      }
   }
}

/*---------------------------------------------------------------------------
* freejarcs - free all arcs in a bin
*---------------------------------------------------------------------------
*/

void
Subdivider::freejarcs(Bin& bin)
{
   bin.adopt(); /* XXX - should not be necessary*/

   Arc_ptr jarc;
   while((jarc=bin.removearc())!=NULL)
   {
      if (jarc->pwlArc)
      {
         jarc->pwlArc->deleteMe(pwlarcpool); jarc->pwlArc=0;
      }
      if (jarc->bezierArc)
      {
         jarc->bezierArc->deleteMe(bezierarcpool); jarc->bezierArc=0;
      }
      jarc->deleteMe(arcpool);
   }
}

/*----------------------------------------------------------------------------
* tessellate - tessellate all Bezier arcs in a bin
*              1) only accepts linear Bezier arcs as input 
*              2) the Bezier arcs are stored in the pwlArc structure
*              3) only vertical or horizontal lines work
*              -- should
*              1) represent Bezier arcs in BezierArc structure
*                 (this requires a multitude of changes to the code)
*              2) accept high degree Bezier arcs (hard)
*              3) map the curve onto the surface to determine tessellation
*              4) work for curves of arbitrary geometry
*----------------------------------------------------------------------------
*/

void Subdivider::tessellate(Bin& bin, REAL rrate, REAL trate, REAL lrate, REAL brate)
{
   for (Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc())
   {
      if (jarc->isbezier())
      {
         assert(jarc->pwlArc->npts==2);
         TrimVertex* pts=jarc->pwlArc->pts;
         REAL s1=pts[0].param[0];
         REAL t1=pts[0].param[1];
         REAL s2=pts[1].param[0];
         REAL t2=pts[1].param[1];

         jarc->pwlArc->deleteMe(pwlarcpool); jarc->pwlArc=0;

         switch(jarc->getside())
         {
            case arc_left:
                 assert(s1==s2);
                 arctessellator.pwl_left(jarc, s1, t1, t2, lrate);
                 break;
            case arc_right:
                 assert(s1==s2);
                 arctessellator.pwl_right(jarc, s1, t1, t2, rrate);
                 break;
            case arc_top:
                 assert(t1==t2);
                 arctessellator.pwl_top(jarc, t1, s1, s2, trate);
                 break;
            case arc_bottom:
                 assert(t1==t2);
                 arctessellator.pwl_bottom(jarc, t1, s1, s2, brate);
                 break;
            case arc_none:
                 (void)abort();
                 break;
         }
         assert(! jarc->isbezier());
         assert(jarc->check()!=0);
      }
   }
}
